﻿//-----------------------------------------------------------------------
// <copyright file="CodeReview.cs" company="Microsoft Corporation">
//     Copyright (c) Microsoft Corporation 2011. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Internal.TeamFoundationServer.Submit
{
    using System;
    using System.Data.SqlClient;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Windows.Forms;
    using Microsoft.TeamFoundation;
    using Properties;

    /// <summary>
    /// CodeReview class.
    /// </summary>
    internal partial class CodeReview : WizardControlTemplate
    {
        /// <summary>
        /// Review not started const.
        /// </summary>
        private const string State0 = "Review not started.";

        /// <summary>
        /// Waiting reviewers const.
        /// </summary>
        private const string State1 = "Waiting reviewers.";

        /// <summary>
        /// Review in progress const.
        /// </summary>
        private const string State2 = "Review in progress.";

        /// <summary>
        /// Review is closed const.
        /// </summary>
        private const string State3 = "Review is closed.";

        /// <summary>
        /// Initializes a new instance of the <see cref="CodeReview"/> class.
        /// </summary>
        public CodeReview()
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.InitializeComponent();

                this.cmbTFSServer.Items.Clear();
                foreach (var item in TeamFoundationHelper.GetRegisteredCollectionNames())
                {
                    this.cmbTFSServer.Items.Add(item);
                }

                Utilities.FixComboItemsWidth(this, this.cmbTFSServer);

                if (this.cmbTFSServer.Items.Count > 0)
                {
                    Utilities.GetDefaultValue(this.cmbTFSServer, Settings.Default.TfsSourceServer);
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Update window.
        /// </summary>
        /// <param name="title">Window Title.</param>
        /// <param name="description">Window Description.</param>
        /// <param name="backButtonEnabled">Back Button Enabled?</param>
        /// <param name="nextButtonEnabled">Next Button Enabled?</param>
        /// <param name="nextButtonCaption">Next Button Caption.</param>
        /// <param name="action">Action to perform on control.</param>
        public override void UpdateWindow(Label title, RichTextBox description, out bool backButtonEnabled, out bool nextButtonEnabled, out string nextButtonCaption, object action)
        {
            backButtonEnabled = true;
            nextButtonCaption = Resources.CodeReviewButtonCaption;
            nextButtonEnabled = this.gridShelveSets.SelectedRows.Count > 0 && this.gridShelveSets.SelectedRows[0].Cells["colStatus"].Value.ToString() != State3;

            title.Text = Resources.CodeReviewTitle;
            description.Text = Resources.CodeReviewDescription;
        }

        /// <summary>
        /// Continue Button event.
        /// </summary>
        public override void NextStep()
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.txtAlias.Text += Utilities.NameSeparator + this.txtCCAlias.Text;

                Utilities.ValidateUser(this.txtAlias, new KeyEventArgs(Keys.Control | Keys.K));
                Utilities.ValidateUser(this.txtCCAlias, new KeyEventArgs(Keys.Control | Keys.K));

                this.txtCCAlias.Text = string.Empty;

                var arguments = string.Format(
                    CultureInfo.InvariantCulture,
                    "\"{0}\" --invite \"{1}\" --sourcecontrol \"TFS\" --server \"{2}\" --workspace \"{3}\" --tfsdiffsource \"shelf\" --database \"{4}\"",
                    this.gridShelveSets.SelectedRows[0].Cells["colShelveSet"].Value,
                    this.txtAlias.Text.Replace(Utilities.NameSeparator, ' '),
                    this.cmbTFSServer.Text,
                    this.cmbWorkspace.Text,
                    TfsSubmitConfig.MalevichServerName);

                string output = string.Empty;

                // Send code review.
                using (var process = new Process())
                {
                    if (!File.Exists(TfsSubmitConfig.MalevichTool))
                    {
                        MessageBox.Show(
                            Resources.CouldNotFindCodeReviewApp + Environment.NewLine + TfsSubmitConfig.MalevichTool,
                            Resources.ErrorString,
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error,
                            MessageBoxDefaultButton.Button1,
                            MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);

                        return;
                    }

                    process.StartInfo.FileName = TfsSubmitConfig.MalevichTool;
                    process.StartInfo.Arguments = arguments;
                    process.StartInfo.UseShellExecute = false;
                    process.StartInfo.RedirectStandardOutput = true;
                    process.StartInfo.CreateNoWindow = true;
                    process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

                    if (process.Start())
                    {
                        output = process.StandardOutput.ReadToEnd();
                        process.WaitForExit();

                        var lines = output.Split('\n');
                        for (var i = lines.Length - 1; i > 0; i--)
                        {
                            if (lines[i].Trim().Length < 0 || !lines[i].Trim().Contains(string.Format(CultureInfo.InvariantCulture, "Change {0} is ready for review.", this.gridShelveSets.SelectedRows[0].Cells["colShelveSet"].Value)))
                            {
                                continue;
                            }

                            MessageBox.Show(
                                lines[i].Trim(),
                                Resources.InformationString,
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Information,
                                MessageBoxDefaultButton.Button1,
                                MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);

                            // HACK - GIVE REVIEWERS VOTE PERMISSIONS
                            // Malevich has a bad mail workflow when requesting to be a voting reviewer.
                            // To avoid reviewers need to request vote permission we add them on the reviewers table.
                            this.GrantVotePermission();

                            // Open Code Review
                            var changeListId = MalevichDal.GetChangeListId(this.gridShelveSets.SelectedRows[0].Cells["colShelveSet"].Value.ToString(), this.cmbWorkspace.Text);
                            MalevichDal.OpenCodeReviewInBrowser(changeListId);

                            this.FireGoHomeRequested(null /* action */);
                            return;
                        }
                    }
                }

                MessageBox.Show(
                    Resources.UnableToStartCodeReviewString + Environment.NewLine + output,
                    Resources.ErrorString,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error,
                    MessageBoxDefaultButton.Button1,
                    MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Select TFS Collection.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbTfsServerSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.cmbWorkspace.Items.Clear();
                this.gridShelveSets.Rows.Clear();

                if (this.cmbTFSServer.Text.Length == 0)
                {
                    return;
                }

                Utilities.SetDefaultValue("TfsSourceServer", this.cmbTFSServer.Text);

                // Retrieve User workspaces for specific project collection.
                foreach (var item in TeamFoundationHelper.GetWorkspaceNames(this.cmbTFSServer.Text))
                {
                    this.cmbWorkspace.Items.Add(item);
                }

                Utilities.FixComboItemsWidth(this, this.cmbWorkspace);

                if (this.cmbWorkspace.Items.Count > 0)
                {
                    Utilities.GetDefaultValue(this.cmbWorkspace, Settings.Default.TfsSourceWorkspace);
                }
            }
            catch (TeamFoundationServiceUnavailableException err)
            {
                MessageBox.Show(
                   err.Message,
                   Resources.UnhandledException,
                   MessageBoxButtons.OK,
                   MessageBoxIcon.Error,
                   MessageBoxDefaultButton.Button1,
                   MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Select Workspace.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbWorkspaceSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.gridShelveSets.Rows.Clear();

                if (this.cmbWorkspace.Text.Length == 0)
                {
                    return;
                }

                Utilities.SetDefaultValue("TfsSourceWorkspace", this.cmbWorkspace.Text);

                var shelvesets = TeamFoundationHelper.GetShelvesets(this.cmbTFSServer.Text, this.cmbWorkspace.Text);
                foreach (var shelveset in shelvesets)
                {
                    var changesAlreadyInReviewInAnotherWorkspace = false;
                    foreach (var workspace in this.cmbWorkspace.Items)
                    {
                        if (workspace.ToString() == this.cmbWorkspace.Text)
                        {
                            continue;
                        }

                        if (MalevichDal.GetChangeListId(shelveset.Name, workspace.ToString()) != -1)
                        {
                            changesAlreadyInReviewInAnotherWorkspace = true;
                        }
                    }

                    if (changesAlreadyInReviewInAnotherWorkspace)
                    {
                        continue;
                    }

                    var state = State0;
                    var changeListId = MalevichDal.GetChangeListId(shelveset.Name, this.cmbWorkspace.Text);
                    if (changeListId != -1)
                    {
                        var changeListStage = MalevichDal.GetChangeListStage(changeListId);

                        if (changeListStage == 2)
                        {
                            // Code Review is closed.
                            state = State3;
                        }
                        else
                        {
                            // Get code review status.
                            state = MalevichDal.ReviewHasFeedback(changeListId) ? State2 : State1;
                        }
                    }

                    // Add Change to the grid.
                    this.gridShelveSets.Rows.Add(shelveset.Name, shelveset.Comment, shelveset.CreationDate, state, changeListId);
                }
            }
            catch (SqlException err)
            {
                MessageBox.Show(
                   err.Message,
                   Resources.ErrorString,
                   MessageBoxButtons.OK,
                   MessageBoxIcon.Information,
                   MessageBoxDefaultButton.Button1,
                   MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Grid SelectionChanged event
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void GridShelveSetsSelectionChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                // Only allow continue for non started reviews.
                this.FireChangeContinueButtonEnabled(this.gridShelveSets.SelectedRows.Count > 0 && this.gridShelveSets.SelectedRows[0].Cells["colStatus"].Value.ToString() != State3);

                if (this.gridShelveSets.Rows.Count == 0 || this.gridShelveSets.SelectedRows.Count == 0)
                {
                    return;
                }

                // Get current reviewers.
                this.txtAlias.Text = MalevichDal.GetInvitedReviewers((int)this.gridShelveSets.SelectedRows[0].Cells["colID"].Value);
                Utilities.ValidateUser(this.txtAlias, new KeyEventArgs(Keys.Control | Keys.K));

                this.txtCCAlias.Text = string.Empty;
                if (this.txtAlias.Text.Length == 0)
                {
                    this.txtCCAlias.Text = TfsSubmitConfig.CodeReviewAliases;
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// GIVE REVIEWERS VOTE PERMISSIONS
        /// </summary>
        private void GrantVotePermission()
        {
            var names = Utilities.ExpandDistributionLists(this.txtAlias.Text).Split(Utilities.NameSeparator);
            foreach (var name in names.Where(name => name != Environment.UserName))
            {
                MalevichDal.GrantVotePermission(this.gridShelveSets.SelectedRows[0].Cells["colShelveSet"].Value.ToString(), this.cmbWorkspace.Text, name.Trim());
            }
        }

        /// <summary>
        /// Alias Key down event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void AliasKeyDown(object sender, KeyEventArgs e)
        {
            Utilities.ValidateUser(sender, e);
        }

        /// <summary>
        /// Grid double click event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void GridShelveSetsDoubleClick(object sender, EventArgs e)
        {
            if (this.gridShelveSets.SelectedRows.Count == 0 || this.gridShelveSets.SelectedRows[0].Cells["colID"].Value.ToString() == "-1")
            {
                return;
            }

            // Open Code Review
            MalevichDal.OpenCodeReviewInBrowser(int.Parse(this.gridShelveSets.SelectedRows[0].Cells["colID"].Value.ToString(), CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// Grid right click event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void GridShelveSetsMouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right && this.gridShelveSets.SelectedRows.Count > 0)
            {
                this.codeReviewOpenMenuItem.Enabled = this.gridShelveSets.SelectedRows[0].Cells["colStatus"].Value.ToString() != State0;
                this.codeReviewCloseMenuItem.Enabled = this.gridShelveSets.SelectedRows[0].Cells["colStatus"].Value.ToString() != State0 && this.gridShelveSets.SelectedRows[0].Cells["colStatus"].Value.ToString() != State3;
                this.codeReviewActivateMenuItem.Enabled = this.gridShelveSets.SelectedRows[0].Cells["colStatus"].Value.ToString() == State3;

                this.codeReviewMenu.Show(Cursor.Position.X, Cursor.Position.Y);
            }
        }

        /// <summary>
        /// Cell Mouse Click event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void GridShelveSetsCellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.RowIndex >= 0)
            {
                this.gridShelveSets.Rows[e.RowIndex].Selected = true;
            }
        }

        /// <summary>
        /// Open Code Review in Web Browser.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CodeReviewOpenMenuItemClick(object sender, EventArgs e)
        {
            this.GridShelveSetsDoubleClick(sender, e);
        }

        /// <summary>
        /// Close Code Review.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CodeReviewCloseMenuItemClick(object sender, EventArgs e)
        {
            MalevichDal.CloseCodeReview(this.gridShelveSets.SelectedRows[0].Cells["colShelveSet"].Value.ToString());
            var index = this.gridShelveSets.SelectedRows[0].Index;
            this.CmbWorkspaceSelectedIndexChanged(sender, e);
            this.gridShelveSets.Rows[index].Selected = true;
        }

        /// <summary>
        /// CodeReview Activate MenuItem Click.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CodeReviewActivateMenuItemClick(object sender, EventArgs e)
        {
            MalevichDal.ActivateCodeReview(this.gridShelveSets.SelectedRows[0].Cells["colShelveSet"].Value.ToString());
            var index = this.gridShelveSets.SelectedRows[0].Index;
            this.CmbWorkspaceSelectedIndexChanged(sender, e);
            this.gridShelveSets.Rows[index].Selected = true;
        }
    }
}
